library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;


entity arithmetic_logic_unit is
generic (	N : integer := 32;
		M : integer := 5);
port(	left		: in  std_logic_vector (N-1 downto 0);	-- left operand (A or NPC)
		right		: in  std_logic_vector (N-1 downto 0);	-- right operand (B or Imm)
		alu_mux	: in  std_logic_vector (3 downto 0);	-- operation to perform (activates the correct output)
		sh_amt	: in  std_logic_vector (M-1 downto 0);	-- shift amount
		info 		: in  std_logic;								-- signed/unsigned, positive/negative, left/right
		types		: in  std_logic;								-- addition/subtraction, logical/arithmetical
		extra		: in  std_logic;								-- shift/rotate, logical-op
		output	: out std_logic_vector (N-1 downto 0);	-- ALU output
		status	: out std_logic								-- unused (should set the OF flag)
);
end arithmetic_logic_unit;

architecture Behavioral of arithmetic_logic_unit is
component adder_evo is
generic (	N : integer := 16;
		M : integer := 4);
port(	A	: in   std_logic_vector (N-1 downto 0);
		B	: in   std_logic_vector (N-1 downto 0);
		C_in	: in   std_logic;
		SUB	: in   std_logic;
		SIGN: in   std_logic;
		OVF	: out std_logic;
		S 	: out std_logic_vector (N-1 downto 0)
);
end component;

component mult is
generic (	N : integer := 32);
port(	A 	: in   std_logic_vector (N/2-1 downto 0);
		B 	: in   std_logic_vector (N/2-1 downto 0);
		sign	: in   std_logic;
		M	: out std_logic_vector (N-1 downto 0)
);
end component;

component logics is
generic (N : integer := 32);
port(	data_reg	: in  std_logic_vector (N-1 downto 0);
		immediate: in  std_logic_vector (N-1 downto 0);
		op_type	: in  std_logic_vector (1 downto 0);
		negation	: in  std_logic;
		data_out	: out std_logic_vector (N-1 downto 0)
);
end component;

component shifter is
generic (	N 			: natural := 32;
		N_SHAMT	: natural := 5 );
port (	ARITH	: in   STD_LOGIC;			-- '0' means logIcal shift, '1' means arithmetic shift
		DIR		: in   STD_LOGIC;			-- '0' means left shift/rotate, '1' means right shift/rotate
		ROT		: in   STD_LOGIC;			-- '0' means simple shift, '1' means rotation
		D_IN	: in   STD_LOGIC_VECTOR (N-1 downto 0);
		SHAMT	: in   STD_LOGIC_VECTOR (N_SHAMT-1 downto 0);
		D_OUT	: out STD_LOGIC_VECTOR (N-1 downto 0) 
);
end component;

component divisor is
generic (N : integer := 32);
port(	A	: in   std_logic_vector (N-1 downto 0); -- dividend
		B	: in   std_logic_vector (N-1 downto 0); -- divisor
		sgn	: in   std_logic;
		Rs	: out std_logic_vector (N-1 downto 0); -- result
		Rm	: out std_logic_vector (N-1 downto 0); -- remainder
		ovf	: out std_logic
);
end component;

signal status_addsub	: std_logic;
signal status_div		: std_logic;
signal log_action		: std_logic_vector (1 downto 0);

signal left_op			: std_logic_vector (N-1 downto 0);
signal right_op		: std_logic_vector (N-1 downto 0);

signal adder_out 		: std_logic_vector (N-1 downto 0);
signal mult_out 		: std_logic_vector (N-1 downto 0);
signal div_out			: std_logic_vector (N-1 downto 0);
signal mod_out		: std_logic_vector (N-1 downto 0);
signal logical_out		: std_logic_vector (N-1 downto 0);
signal shift_out		: std_logic_vector (N-1 downto 0);
signal const_shift_out	: std_logic_vector (N-1 downto 0);
begin

process (left,right,alu_mux,info,types,extra,sh_amt,adder_out,mult_out,div_out,mod_out,logical_out,const_shift_out,shift_out,status_addsub,status_div)
begin
	
	log_action <= types & extra;
	status <= '0';
	
	left_op  <= left;
	right_op <= right;
	
	case alu_mux is
		when "0001" => 					-- addition or subtraction
			output <= adder_out;
			status <= status_addsub;
		when "0010" => 					-- multiplication
			output <= mult_out;
		when "0011" => 					-- division
			output <= div_out;
			status <= status_div;
		when "0100" => 					-- logics
			output <= logical_out;
		when "0101" => 					-- shift a constant value
			output <= const_shift_out;
		when "0110" => 					-- connection to register
			output <= left_op;
		when "0111" => 					-- connect shiftout to addin
			output <= adder_out;
			right_op <= const_shift_out; 
		when "1000" => 					-- SHIFT
			output <= shift_out;
		when "1001" =>					-- DIV
			output <= mod_out;
		when "1010" =>					-- MOV
			output <= right_op;
		when others =>
			output <= (others => '0');
	end case;

end process;

ADD_SUB	: adder_evo generic map(N,M) port
map (	A		=> left_op,
		B		=> right_op,
		C_in		=> '0',
		SUB		=> types,
		sign		=> info,
		OVF		=> status_addsub,
		S		=> adder_out
);

MUL		: mult generic map(N) port
map (	A 		=> left_op (N/2-1 downto 0),
		B 		=> right_op (N/2-1 downto 0),
		sign	=> info,
		M		=> mult_out
);

LOG 	: logics generic map(N) port
map (	data_reg	=> left_op,
		immediate=> right_op,
		op_type	=> log_action,
		negation	=> info,
		data_out => logical_out
);

SHIFT 	: shifter generic map(N,M) port
map (	ARITH => types,
		DIR	=> info,
		ROT   => extra,
		D_IN  => left_op,
		SHAMT => sh_amt,
		D_OUT => shift_out
);

DIV		: divisor generic map(N) port
map (	A  	=> left_op,
		B  	=> right_op,
		sgn	=> info,
		Rs 	=> div_out,
		Rm 	=> mod_out,
		ovf	=> status_div
);

-- this always shifts of two positions (for branch operations)
const_shift_out <= right(N-3 downto 0) & "00";

end Behavioral;
